﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Globalization;
using System.Text.RegularExpressions;
using System.IO;

namespace SernaJson
{
    internal static class Serializers
    {

        /// <summary>
        /// 
        /// </summary>
        static internal class InternalJSONSerializer
        {
            //SerializationSettings ss = new SerializationSettings();
            const string objStart = "{";
            const string objStop = "}";
            const string kvDelimiter = ":";

            //public string Serialize(object obj)
            //{
            //    //ss = settings;
            //   // StringBuilder attribute = new StringBuilder();
            //    return SerializeObject(obj);
            //}
            /// <summary>
            /// 
            /// </summary>
            /// <param name="obj"></param>
            /// <returns></returns>
            public static void SerializeObject(object obj, SerializationSettings ss, StringBuilder sb)
            {
                //Must be supplied from the class

                
                // bool skipUnnecesarySymbols = false;
                //var ttt = obj.GetType();
                //ObjectDefinition od = ObjectsFactory.GetObjectDefinition(obj.GetType());
                //ObjectDefinition od = new ObjectDefinition() { Description = ObjectDefinition.eObjectDescription.CustomClass };


                ObjectDefinition od = ObjectsFactory.GetObjectDefinition(obj);
                
               
                object ho = null;
                object key = null;
                object val = null;


                //StringBuilder1 sb = new StringBuilder1();                
                //attribute.Clear();
                
                //string internalObject = "";                
                bool notFirstProperty = true;
                                
                switch (od.Description)
                {
                    case ObjectDefinition.eObjectDescription.CustomClass:

                        sb.Append(objStart);

                        #region "CUSTOM CLASS"

                        foreach (var property in od.Properties)
                        {
                            if (notFirstProperty)
                                notFirstProperty = false;
                            else
                                sb.Append(ss.Delimiter);

                            //Writing key (key is property name (string))
                            //WriteKeyToString(sb, TypeString, property.Key, null, ss);
                            // sb.Append(String.Concat(ss.Framing, property.Key, ss.Framing, kvDelimiter));
                            sb.Append(ss.Framing);
                            sb.Append(property.Key);
                            sb.Append(ss.Framing);
                            sb.Append(kvDelimiter);

                            //Getting object
                            ho = property.Value.Getter(obj);

                            if (ho != null)
                            {
                                if (property.Value.TypeConverterToString != null)
                                {
                                    //Primitive type. ObjectsFactory must always have converter 
                                    //If we want to setup a cap for the type in must have converter but do nothing
                                    //Empty (null) converter is returned for complex types

                                    property.Value.TypeConverterToString(sb, ho, ss);
                                }
                                else
                                {
                                    //Complex type 
                                    SerializeObject(ho, ss, sb);

                                }

                            }
                            else
                            {
                                //here ; is NECESSARY, because of NULL value termination
                                sb.Append(ss.NullIdentifier);
                            }
                        }
                        #endregion

                        sb.Append(objStop);

                        break;
                    case ObjectDefinition.eObjectDescription.Dictionary:

                        sb.Append("{");

                        #region "DICTIONARY"

                        foreach (var el in (IDictionary)obj)
                        {
                            if (notFirstProperty)
                                notFirstProperty = false;
                            else
                                sb.Append(ss.Delimiter);

                            key = ((DictionaryEntry)el).Key;
                            val = ((DictionaryEntry)el).Value;

                            //Writing key                           
                           // WriteKeyToString(sb, TypeString, Convert.ToString(key), null, ss);
                            sb.Append(ss.Framing);
                            sb.Append(key);
                            sb.Append(ss.Framing);

                            sb.Append(":");

                            if (val != null)
                            {
                                if (od.valueTypeToStringConverter == null)
                                {

                                    //Here we got complex object
                                    SerializeObject(val, ss, sb);  
                                    //internalObject = SerializeObject(val,ss);  
                                    //sb.Append(internalObject);                                   
                                }
                                else
                                {
                                    //WriteValueToString(sb, od.GenericArgument2, val, od.valueTypeToStringConverter, ss);
                                    od.valueTypeToStringConverter(sb, val, ss);
                                    //converterValue(sb, val);
                                }
                                                               
                            }
                            else
                            {                               
                                sb.Append(ss.NullIdentifier);
                            }
                        }

                        #endregion

                        sb.Append("}");

                        break;

                    case ObjectDefinition.eObjectDescription.List:

                        sb.Append("[");

                        #region "LIST"

                        foreach (var val1 in (IEnumerable)obj)
                        {
                            if (notFirstProperty)
                                notFirstProperty = false;
                            else
                                sb.Append(ss.Delimiter);

                            if (val1 != null)
                            {
                                if (od.valueTypeToStringConverter == null)
                                {

                                    //Here we got complex object                  
                                    SerializeObject(val1, ss, sb);
                                    //internalObject = SerializeObject(val1,ss, sb);                                                                      
                                    //sb.Append(internalObject);                                   
                                }
                                else
                                {
                                    //WriteValueToString(sb, od.GenericArgument1, val1, od.valueTypeToStringConverter, ss);
                                    od.valueTypeToStringConverter(sb, val1, ss);
                                }

                            }
                            else
                            {                              
                                sb.Append(ss.NullIdentifier);
                            }
                        }

                        #endregion

                        sb.Append("]");

                        break;
                    case ObjectDefinition.eObjectDescription.HashSet:

                        sb.Append("[");

                        #region "HashSet"


                        foreach (var val1 in (IEnumerable)obj)
                        {
                            if (notFirstProperty)
                                notFirstProperty = false;
                            else
                                sb.Append(ss.Delimiter);


                            if (val1 != null)
                            {
                                if (od.valueTypeToStringConverter == null)
                                {

                                    //Here we got complex object    
                                    SerializeObject(val1, ss, sb);
                                    //internalObject = SerializeObject(val1,ss);                                                                                                           
                                    //sb.Append(internalObject);                                   
                                }
                                else
                                {
                                    //WriteValueToString(sb, od.GenericArgument1, val1, od.valueTypeToStringConverter, ss);
                                    od.valueTypeToStringConverter(sb, val1, ss);
                                }

                            }
                            else
                            {
                                
                                sb.Append(ss.NullIdentifier);
                            }
                        }

                        #endregion

                        sb.Append("]");

                        break;
                    case ObjectDefinition.eObjectDescription.Array:

                        sb.Append("[");

                        #region ARRAY

                        //Filling attributes for the array
                        Array ar = (Array)obj;                      

                        foreach (var val1 in ar)
                        {
                            if (notFirstProperty)
                                notFirstProperty = false;
                            else
                                sb.Append(ss.Delimiter);

                            if (val1 != null)
                            {
                                if (od.valueTypeToStringConverter == null)
                                {

                                    //Here we got complex object              
                                    SerializeObject(val1, ss, sb);
                                    //internalObject = SerializeObject(val1,ss);                                                                      
                                    //sb.Append(internalObject);
                                    
                                }
                                else
                                {
                                    //WriteValueToString(sb, od.GenericArgument1, val1, od.valueTypeToStringConverter, ss);
                                    od.valueTypeToStringConverter(sb, val1, ss);
                                }
                            
                            }
                            else
                            {                               
                                sb.Append(ss.NullIdentifier);
                            }
                        }

                        #endregion

                        sb.Append("]");

                        break;
                }

                

                //return sb.ToString();
            }


            //static void WriteKeyToString(StringBuilder sb, Type t, object value, Action<StringBuilder, object, SerializationSettings> converter, SerializationSettings ss)
            //{
            //    //sb.Append("\"");
            //    sb.Append(ss.Framing);
            //    sb.Append(value);
            //    sb.Append(ss.Framing);
            //    //sb.Append("\"");
            //}
                     

        }//eoc InternalJSONSerializer





        //internal class InternalJSONSerializerOrig
        //{
        //    SerializationSettings ss = new SerializationSettings();

        //    public string Serialize(object obj)
        //    {
        //        //ss = settings;
        //        StringBuilder attribute = new StringBuilder();
        //        return SerializeObject(obj, attribute);
        //    }
        //    /// <summary>
        //    /// 
        //    /// </summary>
        //    /// <param name="obj"></param>
        //    /// <returns></returns>
        //    string SerializeObject(object obj, StringBuilder attribute)
        //    {
        //        //Must be supplied from the class


        //        // bool skipUnnecesarySymbols = false;

        //        var od = ObjectsFactory.GetObjectDefinition(obj);
        //        object ho = null;   //help object           
        //        object key = null;
        //        object val = null;


        //        StringBuilder sb = new StringBuilder();
        //        //attribute.Clear();

        //        string internalObject = "";
        //        StringBuilder myAttributes = null;
        //        bool notFirstProperty = true;

        //        switch (od.Description)
        //        {
        //            case ObjectDefinition.eObjectDescription.CustomClass:

        //                sb.Append("{");

        //                #region "CUSTOM CLASS"

        //                foreach (var property in od.Properties)
        //                {
        //                    if (notFirstProperty)
        //                        notFirstProperty = false;
        //                    else
        //                        sb.Append(ss.Delimiter);


        //                    //Writing key (key is property name (string))
        //                    //WriteKeyToString(sb, TypeString, property.Key, null, ss);
        //                    sb.Append(ss.Framing);
        //                    sb.Append(property.Key);
        //                    sb.Append(ss.Framing);

        //                    sb.Append(":");

        //                    //Getting object
        //                    ho = property.Value.Getter(obj);

        //                    if (ho != null)
        //                    {
        //                        if (property.Value.TypeConverterToString != null)
        //                        {
        //                            //Primitive type. ObjectsFactory must always have converter 
        //                            //If we want to setup a cap for the type in must have converter but do nothing
        //                            //Empty (null) converter is returned for complex types

        //                            //WriteValueToString(sb, property.Value.Type, ho, property.Value.TypeConverterToString, ss);
        //                            property.Value.TypeConverterToString(sb, ho, ss);
        //                        }
        //                        else
        //                        {
        //                            //Complex type                                   
        //                            myAttributes = new StringBuilder();
        //                            internalObject = SerializeObject(ho, myAttributes);
        //                            if (myAttributes != null && myAttributes.Length > 0)
        //                            {
        //                                sb.Append(myAttributes.ToString());
        //                                sb.Append(":");
        //                            }

        //                            //sb.Append("'");
        //                            sb.Append(internalObject);
        //                            //sb.Append("'");
        //                        }

        //                        //here ; can be skipped for short format
        //                        //if (!skipUnnecesarySymbols)

        //                        //sb.Append(";");
        //                    }
        //                    else
        //                    {
        //                        //here ; is NECESSARY, because of NULL value termination
        //                        sb.Append(ss.NullIdentifier);
        //                    }
        //                }
        //                #endregion

        //                sb.Append("}");

        //                break;
        //            case ObjectDefinition.eObjectDescription.Dictionary:

        //                sb.Append("{");

        //                #region "DICTIONARY"

        //                foreach (var el in (IDictionary)obj)
        //                {
        //                    if (notFirstProperty)
        //                        notFirstProperty = false;
        //                    else
        //                        sb.Append(ss.Delimiter);

        //                    key = ((DictionaryEntry)el).Key;
        //                    val = ((DictionaryEntry)el).Value;

        //                    //Writing key                           
        //                    // WriteKeyToString(sb, TypeString, Convert.ToString(key), null, ss);
        //                    sb.Append(ss.Framing);
        //                    sb.Append(key);
        //                    sb.Append(ss.Framing);

        //                    sb.Append(":");

        //                    if (val != null)
        //                    {
        //                        if (od.valueTypeToStringConverter == null)
        //                        {

        //                            //Here we got complex object
        //                            myAttributes = new StringBuilder();
        //                            internalObject = SerializeObject(val, myAttributes);
        //                            if (myAttributes != null && myAttributes.Length > 0)
        //                            {
        //                                sb.Append(myAttributes.ToString());
        //                                sb.Append(":");
        //                            }

        //                            sb.Append(internalObject);
        //                        }
        //                        else
        //                        {
        //                            //WriteValueToString(sb, od.GenericArgument2, val, od.valueTypeToStringConverter, ss);
        //                            od.valueTypeToStringConverter(sb, val, ss);
        //                            //converterValue(sb, val);
        //                        }

        //                    }
        //                    else
        //                    {
        //                        sb.Append(ss.NullIdentifier);
        //                    }
        //                }

        //                #endregion

        //                sb.Append("}");

        //                break;

        //            case ObjectDefinition.eObjectDescription.List:

        //                sb.Append("[");

        //                #region "LIST"

        //                foreach (var val1 in (IEnumerable)obj)
        //                {
        //                    if (notFirstProperty)
        //                        notFirstProperty = false;
        //                    else
        //                        sb.Append(ss.Delimiter);

        //                    if (val1 != null)
        //                    {
        //                        if (od.valueTypeToStringConverter == null)
        //                        {

        //                            //Here we got complex object
        //                            myAttributes = new StringBuilder();
        //                            internalObject = SerializeObject(val1, myAttributes);
        //                            if (myAttributes != null && myAttributes.Length > 0)
        //                            {
        //                                sb.Append(myAttributes.ToString());
        //                                sb.Append(":");
        //                            }

        //                            sb.Append(internalObject);
        //                        }
        //                        else
        //                        {
        //                            //WriteValueToString(sb, od.GenericArgument1, val1, od.valueTypeToStringConverter, ss);
        //                            od.valueTypeToStringConverter(sb, val1, ss);
        //                        }

        //                    }
        //                    else
        //                    {
        //                        sb.Append(ss.NullIdentifier);
        //                    }
        //                }

        //                #endregion

        //                sb.Append("]");

        //                break;
        //            case ObjectDefinition.eObjectDescription.HashSet:

        //                sb.Append("[");

        //                #region "HashSet"


        //                foreach (var val1 in (IEnumerable)obj)
        //                {
        //                    if (notFirstProperty)
        //                        notFirstProperty = false;
        //                    else
        //                        sb.Append(ss.Delimiter);


        //                    if (val1 != null)
        //                    {
        //                        if (od.valueTypeToStringConverter == null)
        //                        {

        //                            //Here we got complex object
        //                            myAttributes = new StringBuilder();
        //                            internalObject = SerializeObject(val1, myAttributes);
        //                            if (myAttributes != null && myAttributes.Length > 0)
        //                            {
        //                                sb.Append(myAttributes.ToString());
        //                                sb.Append(":");
        //                            }

        //                            sb.Append(internalObject);
        //                        }
        //                        else
        //                        {
        //                            //WriteValueToString(sb, od.GenericArgument1, val1, od.valueTypeToStringConverter, ss);
        //                            od.valueTypeToStringConverter(sb, val1, ss);
        //                        }

        //                    }
        //                    else
        //                    {

        //                        sb.Append(ss.NullIdentifier);
        //                    }
        //                }

        //                #endregion

        //                sb.Append("]");

        //                break;
        //            case ObjectDefinition.eObjectDescription.Array:

        //                sb.Append("[");

        //                #region ARRAY

        //                //Filling attributes for the array
        //                Array ar = (Array)obj;

        //                foreach (var val1 in ar)
        //                {
        //                    if (notFirstProperty)
        //                        notFirstProperty = false;
        //                    else
        //                        sb.Append(ss.Delimiter);

        //                    if (val1 != null)
        //                    {
        //                        if (od.valueTypeToStringConverter == null)
        //                        {

        //                            //Here we got complex object
        //                            myAttributes = new StringBuilder();
        //                            internalObject = SerializeObject(val1, myAttributes);
        //                            if (myAttributes != null && myAttributes.Length > 0)
        //                            {
        //                                sb.Append(myAttributes.ToString());
        //                                sb.Append(":");
        //                            }

        //                            sb.Append(internalObject);

        //                        }
        //                        else
        //                        {
        //                            //WriteValueToString(sb, od.GenericArgument1, val1, od.valueTypeToStringConverter, ss);
        //                            od.valueTypeToStringConverter(sb, val1, ss);
        //                        }

        //                    }
        //                    else
        //                    {
        //                        sb.Append(ss.NullIdentifier);
        //                    }
        //                }

        //                #endregion

        //                sb.Append("]");

        //                break;
        //        }



        //        return sb.ToString();
        //    }


        //    //static void WriteKeyToString(StringBuilder sb, Type t, object value, Action<StringBuilder, object, SerializationSettings> converter, SerializationSettings ss)
        //    //{
        //    //    //sb.Append("\"");
        //    //    sb.Append(ss.Framing);
        //    //    sb.Append(value);
        //    //    sb.Append(ss.Framing);
        //    //    //sb.Append("\"");
        //    //}


        //}//eoc InternalJSONSerializer







        /// <summary>
        /// InternalJsonDeserializer currently
        /// </summary>
        internal class InternalJsonDeserializer
        {            
            SerializationSettings ss = new SerializationSettings();

            public object Deserialize(Type type, string _json)            
            {                
                var od = ObjectsFactory.GetObjectDefinition(type);
                object instance = od.InstanceCreator();
                ObjectDeserializatorFromString op = new ObjectDeserializatorFromString(_json, ss);
                op.MyInstance = instance;
                op.MyInstanceDefinition = od;
                op.Start(0);

                return instance;
            }

            class ObjectDeserializatorFromString
            {
                string _json = "";
                SerializationSettings ss = null;

                public object MyInstance = null;
                public ObjectDefinition MyInstanceDefinition = null;

                

                public ObjectDeserializatorFromString(string _json, SerializationSettings ss)
                {
                    this._json = _json;
                    this.ss = ss;
                }

                enum eState
                {
                    BEGIN,
                    KEY,
                    VALUE,
                    KEY_END,
                    ARRAY,
                    ARRAY_STRING_ELEMENT,
                    ARRAY_OBJECT_ELEMENT,
                    ARRAY_ARRAY_ELEMENT,
                }


                public void Start(int startFrom)
                {
                    eState state = eState.BEGIN;
                    char c;
                    StringBuilder sbKey = new StringBuilder();
                    StringBuilder sbValue = new StringBuilder();
                    bool useNextSymbolAsChar = false;
                    bool valueHasStarted = false;
                    bool objectHasStarted = false;
                    bool arrayHasStarted = false;
                    int openBrackets = 0; //{ or [
                    int openBrackets1 = 0; //{

                    Dictionary<string, string> rt = new Dictionary<string, string>();
                    PropertyDefinition internalPropertyDefinition = null;

                    ObjectDefinition internalInstanceDefinition = null;
                    object internalInstance = null;
                    ObjectDeserializatorFromString internalDeserializator = null;

                    Action Flush = () =>
                    {
                        if (sbKey.Length > 0)
                        {
                            var vs = sbValue.ToString();
                            var ks = sbKey.ToString();

                            internalPropertyDefinition = this.MyInstanceDefinition.GetPropertyDefinitionByName(ks);

                            //Here we always apply values to 
                            switch (this.MyInstanceDefinition.Description)
                            {
                                case ObjectDefinition.eObjectDescription.CustomClass:

                                    //if our object doesn't have such property it must be ignored
                                    if (internalPropertyDefinition != null && internalPropertyDefinition.Setter != null)
                                    {
                                        if (vs.Length == 0 || vs == ss.NullIdentifier)
                                        {
                                            //Setting object to null
                                            internalPropertyDefinition.Setter(
                                                this.MyInstance,
                                                null
                                                );
                                        }

                                        if (internalPropertyDefinition.TypeConverterFromString == null)
                                        {
                                            //Complex type

                                            internalInstanceDefinition = ObjectsFactory.GetObjectDefinition(internalPropertyDefinition.Type);
                                            internalInstance = internalInstanceDefinition.InstanceCreator();
                                            internalDeserializator = new ObjectDeserializatorFromString(vs, ss);
                                            internalDeserializator.MyInstance = internalInstance;
                                            internalDeserializator.MyInstanceDefinition = internalInstanceDefinition;
                                            internalDeserializator.Start(0);

                                            internalPropertyDefinition.Setter(
                                            this.MyInstance,
                                            internalInstance
                                            );
                                        }
                                        else
                                        {
                                            internalPropertyDefinition.Setter(
                                                this.MyInstance,
                                                internalPropertyDefinition.TypeConverterFromString(sbValue.ToString(), ss).Item2
                                                );
                                        }
                                    }
                                    break;
                                //case ObjectDefinition.eObjectDescription.Dictionary:
                                //    break;
                                //case ObjectDefinition.eObjectDescription.HashSet:
                                //    break;
                                //case ObjectDefinition.eObjectDescription.List:
                                //    break;
                                //case ObjectDefinition.eObjectDescription.Array:
                                //    break;
                                //.....go on with Dictionaries, Lists, Hashsets, Arrays L.665
                                default:
                                    throw new Exception("SernaJson.deserialization: Unknown primitive property type");
                            }
                        }

                        sbKey.Clear();
                        sbValue.Clear();
                    };









                    Action FlushArray = () =>
                    {
                        var ks = sbKey.ToString();

                        if (ks.Length > 0)
                        {
                            switch (this.MyInstanceDefinition.Description)
                            {
                                case ObjectDefinition.eObjectDescription.HashSet:

                                    if (ks == ss.NullIdentifier)
                                    {
                                        this.MyInstanceDefinition.HashSet_AddElement(this.MyInstance, null);
                                    }
                                    else
                                    {                                        
                                        if (this.MyInstanceDefinition.valueTypeFromStringConverter == null)
                                        {
                                            //Complex element
                                            
                                            //!!!!!!!!!!!!!!is it possible that this.MyInstanceDefinition.GenericArgument1 doesn't exist ???

                                            //!!!!!!!!!!!!!!MicroOpt is possible, when internalInstanceDefinition is filled right after difining that this is an array
                                            if (internalInstanceDefinition == null)
                                                internalInstanceDefinition = ObjectsFactory.GetObjectDefinition(this.MyInstanceDefinition.GenericArgument1);

                                            internalInstance = internalInstanceDefinition.InstanceCreator();
                                            internalDeserializator = new ObjectDeserializatorFromString(ks, ss);
                                            internalDeserializator.MyInstance = internalInstance;
                                            internalDeserializator.MyInstanceDefinition = internalInstanceDefinition;
                                            internalDeserializator.Start(0);

                                            this.MyInstanceDefinition.HashSet_AddElement(this.MyInstance, internalInstance);

                                        }
                                        else
                                        {
                                            this.MyInstanceDefinition.HashSet_AddElement
                                            (
                                                this.MyInstance,
                                                this.MyInstanceDefinition.valueTypeFromStringConverter(ks, ss).Item2
                                            );
                                        }
                                    }

                                    break;
                                case ObjectDefinition.eObjectDescription.CustomClass:
                                    break;
                                case ObjectDefinition.eObjectDescription.Dictionary:
                                    break;
                                case ObjectDefinition.eObjectDescription.List:
                                    break;
                                case ObjectDefinition.eObjectDescription.Array:
                                    break;
                                default:

                                    break;
                            }

                            sbKey.Clear();
                        }
                    };







                    char groupDelimiter = ':';

                    for (int i = startFrom; i < _json.Length; i++)
                    {
                        c = _json[i];
                        switch (state)
                        {
                            case eState.BEGIN:

                                if (c == ss.Framing)    // " or ' (auto discovery)
                                {
                                    state = eState.KEY;
                                    continue;
                                }
                                else if (c == '{' || c == ' ' || c == '\r' || c == '\n' || c == '\t') //,
                                    continue;
                                else if (c == ss.Delimiter) // , or ;
                                {

                                    continue;
                                }
                                else if (c == '}')
                                    return;     //Finish survey
                                else if (c == '[')
                                {
                                    state = eState.ARRAY;
                                    openBrackets += 1;
                                    continue;
                                }
                                else
                                    throw new Exception("Serna.dll. DeserializeJSON->WRONG START");

                            case eState.KEY:

                                if (useNextSymbolAsChar)
                                {
                                    sbKey.Append(c);
                                    useNextSymbolAsChar = false;
                                    continue;
                                }
                                if (c == ss.Framing)                //"                
                                {
                                    state = eState.KEY_END;
                                }
                                else if (c == '\\')
                                {
                                    useNextSymbolAsChar = true;
                                }
                                else
                                {
                                    sbKey.Append(c);
                                }
                                continue;

                            case eState.KEY_END:    //Must come value or next key or end of object
                                if (c == groupDelimiter)    //":"
                                    state = eState.VALUE;
                                else if (c == ' ' || c == '\r' || c == '\n' || c == '\t')
                                    continue;
                                else if (c == ss.Delimiter)
                                {
                                    Flush();
                                    state = eState.BEGIN;     //Getting next key                  
                                }
                                else if (c == '}')
                                {
                                    Flush();
                                    return;     //Finish survey
                                }
                                else
                                    throw new Exception("SernaJson.dll. DeserializeJSON->WRONG KEY_END");

                                continue;

                            case eState.VALUE:
                                if (useNextSymbolAsChar)
                                {
                                    sbValue.Append(c);
                                    useNextSymbolAsChar = false;
                                    continue;
                                }

                                if (valueHasStarted)
                                {
                                    if (c == ss.Framing)    //"
                                    {
                                        Flush();
                                        valueHasStarted = false;
                                        state = eState.BEGIN;
                                    }
                                    else if (c == '\\')
                                    {
                                        useNextSymbolAsChar = true;
                                    }
                                    else
                                        sbValue.Append(c);
                                }
                                else if (arrayHasStarted)
                                {
                                    if (c == '[')
                                    {
                                        sbValue.Append(c);
                                        openBrackets += 1;
                                    }
                                    else if (c == ']')
                                    {
                                        if ((openBrackets - 1) == 0)
                                        {
                                            arrayHasStarted = false;
                                            state = eState.BEGIN;
                                            openBrackets = 0;
                                            sbValue.Append(c);
                                            Flush();
                                        }
                                        else
                                        {
                                            sbValue.Append(c);
                                            openBrackets--;
                                        }
                                    }
                                    else if (c == '\\')
                                    {
                                        useNextSymbolAsChar = true;
                                    }
                                    else
                                        sbValue.Append(c);
                                }
                                else if (objectHasStarted)
                                {
                                    if (c == '{')
                                    {
                                        sbValue.Append(c);
                                        openBrackets += 1;
                                    }
                                    else if (c == '}')
                                    {
                                        if ((openBrackets - 1) == 0)
                                        {
                                            objectHasStarted = false;
                                            state = eState.BEGIN;
                                            openBrackets = 0;
                                            sbValue.Append(c);
                                            Flush();
                                        }
                                        else
                                        {
                                            sbValue.Append(c);
                                            openBrackets--;
                                        }
                                    }
                                    else if (c == '\\')
                                    {
                                        useNextSymbolAsChar = true;
                                    }
                                    else
                                        sbValue.Append(c);
                                }
                                else
                                {
                                    //Ignoring all garbage
                                    if (c == ' ' || c == '\r' || c == '\n' || c == '\t')
                                        continue;
                                    else if (c == ss.Delimiter)    //,
                                    {
                                        Flush();
                                        state = eState.BEGIN;
                                    }
                                    else if (c == ss.Framing)   //framer
                                        valueHasStarted = true;
                                    else if (c == '{')
                                    {
                                        sbValue.Append(c);
                                        openBrackets += 1;
                                        objectHasStarted = true;
                                    }
                                    else if (c == '[')
                                    {
                                        sbValue.Append(c);
                                        openBrackets += 1;
                                        arrayHasStarted = true;
                                    }
                                    else if (c == '}')
                                    {
                                        Flush();
                                        return;     //Finish survey
                                    }
                                    else
                                        sbValue.Append(c);  //can be digit without brackets
                                }


                                continue;
                            case eState.ARRAY:

                                if (c == '[')
                                {
                                    //sbKey.Append(c);
                                    //openBrackets += 1;
                                    openBrackets1 = 1;
                                    state = eState.ARRAY_ARRAY_ELEMENT;
                                    sbKey.Append(c);
                                    continue;
                                }
                                else if (c == ']')
                                {
                                    FlushArray();
                                    return;     //Finish survey
                                    //if ((openBrackets - 1) == 0)
                                    //{
                                    //    FlushArray();
                                    //    //Flush();        //-----------------------???
                                    //    return;     //Finish survey
                                    //}
                                    //else
                                    //{
                                    //    sbKey.Append(c);
                                    //    openBrackets--;
                                    //}
                                }
                                else if (c == '{')
                                {
                                    openBrackets1 = 1;
                                    state = eState.ARRAY_OBJECT_ELEMENT;
                                    sbKey.Append(c);
                                    continue;
                                }
                                else if (c == '"')
                                {
                                    state = eState.ARRAY_STRING_ELEMENT;
                                    continue;
                                }
                                else if (c == ss.Delimiter)
                                {
                                    //--------------------------   HERE CAN BE , (array elements delimter) 
                                    FlushArray();
                                }                                    
                                else
                                {
                                    sbKey.Append(c);
                                    //skipping the rest of symbols
                                }

                                continue;
                            case eState.ARRAY_ARRAY_ELEMENT:

                                 if (useNextSymbolAsChar)
                                {
                                    sbKey.Append(c);
                                    useNextSymbolAsChar = false;
                                    continue;
                                }

                                if (c == '[')
                                {
                                    sbKey.Append(c);
                                    openBrackets1 += 1;
                                }
                                else if (c == ']')
                                {
                                    if ((openBrackets1 - 1) == 0)
                                    {
                                        sbKey.Append(c);
                                        openBrackets1 = 0;
                                        FlushArray();
                                        state = eState.ARRAY;
                                    }
                                    else
                                    {
                                        sbKey.Append(c);
                                        openBrackets1--;                                        
                                    }

                                }
                                else if (c == '\\')
                                {
                                    useNextSymbolAsChar = true;
                                }
                                else
                                    sbKey.Append(c);

                                continue;
                            case eState.ARRAY_STRING_ELEMENT:

                                if (useNextSymbolAsChar)
                                {
                                    sbKey.Append(c);
                                    useNextSymbolAsChar = false;
                                    continue;
                                }

                                if (c == '"')
                                {
                                    FlushArray();
                                    state = eState.ARRAY;
                                }
                                else if (c == '\\')
                                {
                                    useNextSymbolAsChar = true;
                                }
                                else
                                    sbKey.Append(c);

                                continue;
                            case eState.ARRAY_OBJECT_ELEMENT:

                                if (useNextSymbolAsChar)
                                {
                                    sbKey.Append(c);
                                    useNextSymbolAsChar = false;
                                    continue;
                                }

                                if (c == '{')
                                {
                                    sbKey.Append(c);
                                    openBrackets1 += 1;
                                }
                                else if (c == '}')
                                {
                                    if ((openBrackets1 - 1) == 0)
                                    {
                                        sbKey.Append(c);
                                        openBrackets1 = 0;
                                        FlushArray();
                                        state = eState.ARRAY;
                                    }
                                    else
                                    {
                                        sbKey.Append(c);
                                        openBrackets1--;                                        
                                    }

                                }
                                else if (c == '\\')
                                {
                                    useNextSymbolAsChar = true;
                                }
                                else
                                    sbKey.Append(c);

                                continue;
                        }
                    }

                    Flush();
                    return;     //Finish survey
                }//eo Start
            }


        }//eoc


















        /// <summary>
        /// InternalJsonDeserializer currently to dictionary
        /// </summary>
        internal class InternalJsonDeserializerToDictionary
        {
            string _json = "";

            public Dictionary<string, string> Deserialize(string str)
            {
                _json = str;
                return Start(0);

            }

            enum eState
            {
                BEGIN,
                KEY,
                VALUE,
                KEY_END,
                ARRAY,
                ARRAY_STRING_ELEMENT,
                ARRAY_OBJECT_ELEMENT,
            }


            Dictionary<string, string> Start(int startFrom)
            {
                eState state = eState.BEGIN;
                char c;
                StringBuilder sbKey = new StringBuilder();
                StringBuilder sbValue = new StringBuilder();
                bool useNextSymbolAsChar = false;
                bool valueHasStarted = false;
                bool objectHasStarted = false;
                bool arrayHasStarted = false;
                int openBrackets = 0; //{ or [
                int openBrackets1 = 0; //{
                Dictionary<string, string> rt = new Dictionary<string, string>();

                Action Flush = () =>
                {
                    if (sbKey.Length > 0)
                        rt[sbKey.ToString()] = (sbValue.Length > 0) ? sbValue.ToString() : String.Empty;
                    sbKey.Clear();
                    sbValue.Clear();
                };


                Action FlushArray = () =>
                {
                    if (sbKey.Length > 0)
                        rt[sbKey.ToString()] = String.Empty;

                    sbKey.Clear();
                };

                char delimiter = ',';    //----------DONT CHANGE THIS VALUE, IT HAS AUTODISCOVERY----------
                char groupDelimiter = ':';
                char framer = '"';  //----------DONT CHANGE THIS VALUE, IT HAS AUTODISCOVERY----------

                for (int i = startFrom; i < _json.Length; i++)
                {
                    c = _json[i];
                    switch (state)
                    {
                        case eState.BEGIN:

                            if (c == framer || c == '\'')    // " or ' (auto discovery)
                            {
                                framer = c;
                                state = eState.KEY;
                                continue;
                            }
                            else if (c == '{' || c == ' ' || c == '\r' || c == '\n' || c == '\t') //,
                                continue;
                            else if (c == delimiter || c == ';') // , or ;
                            {
                                delimiter = c;
                                continue;
                            }
                            else if (c == '}')
                                return rt;     //Finish survey
                            else if (c == '[')
                            {
                                state = eState.ARRAY;
                                openBrackets += 1;
                                continue;
                            }
                            else
                                throw new Exception("Serna.dll. DeserializeJSON->WRONG START");

                        case eState.KEY:

                            if (useNextSymbolAsChar)
                            {
                                sbKey.Append(c);
                                useNextSymbolAsChar = false;
                                continue;
                            }
                            if (c == framer)                //"                
                            {
                                state = eState.KEY_END;
                            }
                            else if (c == '\\')
                            {
                                useNextSymbolAsChar = true;
                            }
                            else
                            {
                                sbKey.Append(c);
                            }
                            continue;

                        case eState.KEY_END:    //Must come value or next key or end of object
                            if (c == groupDelimiter)    //":"
                                state = eState.VALUE;
                            else if (c == ' ' || c == '\r' || c == '\n' || c == '\t')
                                continue;
                            else if (c == delimiter)
                            {
                                delimiter = c;
                                Flush();
                                state = eState.BEGIN;     //Getting next key                  
                            }
                            else if (c == '}')
                            {
                                Flush();
                                return rt;
                            }
                            else if (c == ';')  //Changing delimiter
                            {
                                delimiter = c;
                                Flush();
                                state = eState.BEGIN;     //Getting next key                  
                            }
                            else
                                throw new Exception("Serna.dll. DeserializeJSON->WRONG KEY_END");

                            continue;

                        case eState.VALUE:
                            if (useNextSymbolAsChar)
                            {
                                sbValue.Append(c);
                                useNextSymbolAsChar = false;
                                continue;
                            }

                            if (valueHasStarted)
                            {
                                if (c == framer)    //"
                                {
                                    Flush();
                                    valueHasStarted = false;
                                    state = eState.BEGIN;
                                }
                                else if (c == '\\')
                                {
                                    useNextSymbolAsChar = true;
                                }
                                else
                                    sbValue.Append(c);
                            }
                            else if (arrayHasStarted)
                            {
                                if (c == '[')
                                {
                                    sbValue.Append(c);
                                    openBrackets += 1;
                                }
                                else if (c == ']')
                                {
                                    if ((openBrackets - 1) == 0)
                                    {
                                        arrayHasStarted = false;
                                        state = eState.BEGIN;
                                        openBrackets = 0;
                                        sbValue.Append(c);
                                        Flush();
                                    }
                                    else
                                    {
                                        sbValue.Append(c);
                                        openBrackets--;
                                    }
                                }
                                else if (c == '\\')
                                {
                                    useNextSymbolAsChar = true;
                                }
                                else
                                    sbValue.Append(c);
                            }
                            else if (objectHasStarted)
                            {
                                if (c == '{')
                                {
                                    sbValue.Append(c);
                                    openBrackets += 1;
                                }
                                else if (c == '}')
                                {
                                    if ((openBrackets - 1) == 0)
                                    {
                                        objectHasStarted = false;
                                        state = eState.BEGIN;
                                        openBrackets = 0;
                                        sbValue.Append(c);
                                        Flush();
                                    }
                                    else
                                    {
                                        sbValue.Append(c);
                                        openBrackets--;
                                    }
                                }
                                else if (c == '\\')
                                {
                                    useNextSymbolAsChar = true;
                                }
                                else
                                    sbValue.Append(c);
                            }
                            else
                            {
                                //Ignoring all garbage
                                if (c == ' ' || c == '\r' || c == '\n' || c == '\t')
                                    continue;
                                else if (c == delimiter)    //,
                                {
                                    Flush();
                                    state = eState.BEGIN;
                                }
                                else if (c == framer)   //framer
                                    valueHasStarted = true;
                                else if (c == '{')
                                {
                                    sbValue.Append(c);
                                    openBrackets += 1;
                                    objectHasStarted = true;
                                }
                                else if (c == '[')
                                {
                                    sbValue.Append(c);
                                    openBrackets += 1;
                                    arrayHasStarted = true;
                                }
                                else if (c == '}')
                                {
                                    Flush();
                                    return rt;
                                }
                                else
                                    sbValue.Append(c);  //can be digit without brackets
                            }


                            continue;
                        case eState.ARRAY:

                            if (c == '[')
                            {
                                sbKey.Append(c);
                                openBrackets += 1;
                            }
                            else if (c == ']')
                            {
                                if ((openBrackets - 1) == 0)
                                {
                                    Flush();
                                    return rt;
                                }
                                else
                                {
                                    sbKey.Append(c);
                                    openBrackets--;
                                }
                            }
                            if (c == '{')
                            {
                                openBrackets1 = 1;
                                state = eState.ARRAY_OBJECT_ELEMENT;
                                sbKey.Append(c);
                                continue;
                            }
                            if (c == '"')
                            {
                                state = eState.ARRAY_STRING_ELEMENT;
                                continue;
                            }
                            //skipping the rest of symbols

                            continue;
                       
                        case eState.ARRAY_STRING_ELEMENT:

                            if (useNextSymbolAsChar)
                            {
                                sbKey.Append(c);
                                useNextSymbolAsChar = false;
                                continue;
                            }

                            if (c == '"')
                            {
                                FlushArray();
                                state = eState.ARRAY;
                            }
                            else if (c == '\\')
                            {
                                useNextSymbolAsChar = true;
                            }
                            else
                                sbKey.Append(c);

                            continue;
                        case eState.ARRAY_OBJECT_ELEMENT:

                            if (useNextSymbolAsChar)
                            {
                                sbKey.Append(c);
                                useNextSymbolAsChar = false;
                                continue;
                            }

                            if (c == '{')
                            {
                                sbKey.Append(c);
                                openBrackets1 += 1;
                            }
                            else if (c == '}')
                            {
                                if ((openBrackets1 - 1) == 0)
                                {
                                    sbKey.Append(c);
                                    openBrackets1 = 0;
                                    FlushArray();
                                    state = eState.ARRAY;
                                }
                                else
                                {
                                    sbKey.Append(c);
                                    openBrackets1--;
                                }

                            }
                            else if (c == '\\')
                            {
                                useNextSymbolAsChar = true;
                            }
                            else
                                sbKey.Append(c);

                            continue;
                    }
                }

                Flush();
                return rt;
            }//eo Start


        }//eoc
    }
}
